import axios from "axios";
import express from "express";
import { PrismaClient, Prisma } from ".prisma/client";
import { Check, Result, Time, redis, CreateId, Token } from "@/utils";
import { getAccessToken } from "@/Init";

const prisma = new PrismaClient();
const router = express.Router();

/** 用户登录 */
router.post("/user/login", async (req, res) => {
   const { code }: MpUser.Login = req.body;

   if (!Check.Required([code])) return res.send(Result.Error("参数错误"));

   // 请求微信服务器，获取用户必要信息
   const { data } = await axios.get<WeChat.UserInfo>("https://api.weixin.qq.com/sns/jscode2session", {
      params: {
         grant_type: "authorization_code",
         appid: process.env.APPID,
         secret: process.env.APPSECRET,
         js_code: code
      }
   });

   if (!data) return res.send(Result.Error());
   if (data.errcode) return res.send(Result.Error());

   // 查询是否已存在当前用户
   let user = await prisma.client.findFirst({ where: { openid: data.openid, status: "NORMAL" } });

   // 若用户不存在，则将用户信息存放至数据库
   if (!user) {
      const id = await CreateId.One("client");
      const salt = Math.random().toString(36).slice(-8);

      user = await prisma.client.create({
         data: {
            salt,
            id: id,
            sex: "PRIVACY",
            openid: data.openid,
            status: "NORMAL",
            created_time: Time.Now()
         }
      });
   }

   const token = Token.Encrypt(user.id);

   // 将用户信息转换为 json 字符串
   // 转换后的信息存入 redis 缓存中
   await redis.set(
      user.id.toString(),
      JSON.stringify({
         token,
         openid: data.openid,
         session_key: data.session_key
      })
   );

   res.send(Result.OK({ token }));
});

/** 获取手机号码 */
router.post("/user/getPhoneNumber", async (req, res) => {
   const { code } = req.body as MpUser.GetPhoneNumber;
   if (!Check.Required([code])) res.send(Result.Error("参数错误"));

   const { token } = req.headers;
   const userId = Token.Decrypt(token);

   const acc = await redis.get("access");
   if (!Check.StringIsJSON(acc)) {
      getAccessToken();
      return res.send(Result.Error());
   }
   const access = JSON.parse(acc as string) as Access;

   // 请求微信接口，获取信息
   const { data } = await axios.post(
      `https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=${access.access_token}`,
      { code: code }
   );

   if (data.errcode) {
      await getAccessToken(true);
      return res.send(Result.Error("信息验证失败，请重新登录"));
   }

   // 将用户手机号添加至数据库
   await prisma.client.update({
      where: { id: userId },
      data: {
         phone: data.phone_info.phoneNumber
      }
   });

   res.send(Result.OK());
});

/** 获取用户信息 */
router.post("/user/getUserInfo", async (req, res) => {
   const userId = req.userId;

   const userInfo = await prisma.client.findFirst({
      where: { id: userId, status: "NORMAL" },
      select: {
         /** 用户名 */
         name: true,
         /** 手机号 */
         phone: true,
         /** 头像 */
         head_img: true,
         /** 性别 */
         sex: true,
         /** 签名 */
         sign: true
      }
   });

   if (!userInfo) return res.send(Result.Error("信息获取失败，请重新登录"));

   res.send(Result.OK(userInfo));
});

/** 报名 */
router.post("/common/signup", async (req, res) => {
   const params = req.body as MpUser.Singup;
   if (!Check.Required([params.name, params.phone, params.sex, params.target])) return res.send(Result.Error("参数错误"));

   const hasPhone = await prisma.mp_signup.findMany({ where: { phone: params.phone } });
   if (hasPhone.length > 0) return res.send(Result.Error("当前手机号已报名，请勿重复报名"));

   const { userId } = req;
   const id = await CreateId.One("mp_signup");
   const data = await prisma.mp_signup.create({
      data: {
         id,
         created_by: userId || null,
         created_time: Time.Now(),
         level: params.level || null,
         name: params.name,
         phone: params.phone,
         school: params.school || null,
         sex: params.sex,
         target: params.target
      }
   });

   if (!data) return res.send(Result.Error());

   return res.send(Result.OK());
});

export const mp = router;
